import { _decorator, Component, instantiate, Label, Node, Prefab, Vec3 } from 'cc';
import { PlayerController } from './PlayerController';
const { ccclass, property } = _decorator;

enum BlockType{
    BT_NONE,
    BT_STONE,
};
enum GameState{
    GS_INIT, // 显示游戏菜单，初始化一些资源
    GS_PLAYING, // 隐藏游戏菜单，玩家可以操作角色进行游戏
    GS_END, // 游戏结束，显示结束菜单
};

@ccclass('GameManager')
export class GameManager extends Component {

    // 赛道预制
    @property({type: Prefab})
    public cubePrfb: Prefab | null = null;
    // 赛道长度
    @property
    public roadLength = 50;
    private _road: BlockType[] = [];
    @property({type: PlayerController})
    public playerCtrl: PlayerController | null = null;
    // 为了动态地开启/关闭开始菜单，还需要在 GameManager 脚本中引用 StartMenu 节点
    @property({type: Node})
    public startMenu: Node | null = null;
    @property({type: Label})
    public stepsLabel: Label | null = null;

    start() {
        this.curState = GameState.GS_INIT;
        // this.generateRoad()
        // 监听角色跳跃消息，并调用判断函数 可选链的写法更加的简洁
        this.playerCtrl?.node.on('JumpEnd', this.onPlayerJumpEnd, this);
        
    }

    // 跳转结束
    onPlayerJumpEnd(moveIndex: number) {
        this.checkResult(moveIndex);
    }

    init() {
        // 激活主界面
        if (this.startMenu) {
            this.startMenu.active = true;
        }
        // 生成赛道
        this.generateRoad();
        if(this.playerCtrl){
            // 禁止接收用户操作人物移动指令
            this.playerCtrl.setInputActive(false);
            // 重置人物位置
            this.playerCtrl.node.setPosition(Vec3.ZERO);
            // 重置记录跳了几步
            this.playerCtrl.reset();
        }
    }

    // set curState 这样的存取器可以让我们在设置游戏状态时更加的安全
    set curState (value: GameState) {
        switch(value) {
            case GameState.GS_INIT:
                this.init();
                break;
            case GameState.GS_PLAYING:
                if (this.startMenu) {
                    this.startMenu.active = false;
                }
                // 设置 active 为 true 时会直接开始监听鼠标事件
                // 此时鼠标抬起事件还未派发 会出现的现象就是，游戏开始的瞬间人物已经开始移动 因此，这里需要做延迟处理
                setTimeout(() => {
                    if (this.playerCtrl) {
                        this.playerCtrl.setInputActive(true);
                    }
                }, 0.1);
                break;
            case GameState.GS_END:
                break;
        }
    }

    // 点击开始按钮时
    onStartButtonClicked() {
        this.curState = GameState.GS_PLAYING;
    }

    generateRoad() {
        // 防止游戏重新开始时，赛道还是旧的赛道 因此，需要移除旧赛道，清除旧赛道数据
        this.node.removeAllChildren();
        this._road = [];
        // 确保游戏运行时，人物一定站在实路上
        this._road.push(BlockType.BT_STONE);

        // 确定好每一格赛道类型
        for (let i = 1; i < this.roadLength; i++) {
            // 如果上一格赛道是坑，那么这一格一定不能为坑
            if (this._road[i-1] === BlockType.BT_NONE) {
                this._road.push(BlockType.BT_STONE);
            } else {
                this._road.push(Math.floor(Math.random() * 2));
            }
        }

        // 根据赛道类型生成赛道
        for (let j = 0; j < this._road.length; j++) {
            let block: Node = this.spawnBlockByType(this._road[j]);
            // 判断是否生成了道路，因为 spawnBlockByType 有可能返回坑（值为 null）
            if (block) {
                this.node.addChild(block);
                block.setPosition(j, -1.5, 0);
            }
        }
    }

    spawnBlockByType(type: BlockType) {
        if (!this.cubePrfb) {
            return null;
        }
    
        let block: Node | null = null;
        // 赛道类型为实路才生成
        switch(type) {
            case BlockType.BT_STONE:
                block = instantiate(this.cubePrfb); // instantiate: 是 Cocos Creator 提供的克隆预制体的方法。当然它不仅能克隆预制体，你甚至可以用它克隆别的类型比如某个对象！
                break;
        }
    
        return block;
    }


    // 监听角色跳跃结束事件，并根据规则判断输赢，增加失败和结束判断，如果跳到空方块或是超过了最大长度值都结束
    checkResult(moveIndex: number) {
        let setMoveIndex = moveIndex >= this.roadLength ? this.roadLength : moveIndex // 当前为跳了几步
        if (moveIndex < this.roadLength) {
            // 跳到了坑上
            if (this._road[moveIndex] == BlockType.BT_NONE) {
                this.curState = GameState.GS_INIT;
                setMoveIndex = 0
            }
        } else {    // 跳过了最大长度
            this.curState = GameState.GS_INIT;
            setMoveIndex = 0
        }
        if (this.stepsLabel) {
            // 因为在最后一步可能出现步伐大的跳跃，但是此时无论跳跃是步伐大还是步伐小都不应该多增加分数
            this.stepsLabel.string = '' + setMoveIndex;
        }
    }

    update(deltaTime: number) {
        
    }
}

